问题365网经验知识百科全书!

[Spring源码]——容器基本实现

0次浏览 | 2023-09-01
来源 :daniel

笔记整理自《Spring源码深度解析》(第2版),同时也参考了一些网上资源,具体参考链接在文末

文章目录

核心类介绍

DefaultlistableBeanFactoryXmlBeanDefinitionReader

各个类的功能处理步骤 容器的基础XmlBeanFactory

整体分析

XmlBeanFactory初始化加载bean 源码分析

起始(1)构造 Resource 资源文件(2)使用Resouce来进行XmlBeanFactory的初始化(3) 获取beanDefinition(4) loadBeanDefinitions(5)进入逻辑核心部分(6)获取Xml的验证模式(7)获取document(8) 解析及注册BeanDefinitions(9) 注册bean定义(10)doRegisterBeanDefinitions 流程(11)parseBeanDefinitions 分析 核心类介绍 DefaultlistableBeanFactory

DefaultListableBeanFactmy 是整个 bean 加载的核心部分,是 Spring 注册及加载bean的默认实现

图中各个类的作用:

AliasRegistry:定义对alias的简单增删改等操作。SimpleAliasRegistry:主要使用map作为alias的缓存,并对接口AliasRegistry进行实现。SingletonBeanRegistry:定义对单例的注册及获取。BeanFactory:定义获取bean及bean的各种属性。DefaultSingletonBeanRegistry:对接口SingletonBeanRegistry各函数的实现。HierarchicalBeanFactory:继承BeanFactory,也就是在BeanFactory定义的功能的基础上增加了对parentFactory的支持BeanDefinitionRegistry:定义对BeanDefinition的各种增删改操作FactoryBeanRegistrySupport:在DefaultSingletonBeanRegistry基础上增加了对FactoryBean的特殊处理功能ConfigurableBeanFactory:提供配置Factory的各种方法ListableBeanFactory:根据各种条件获取bean的配置清单。AbstractBeanFactory:综合FactoryBeanRegistrySupport和ConfigurableBeanFactory的功能。AutowireCapableBeanFactory:提供创建bean、自动注入、初始化以及应用bean的后处理器AbstractAutowireCapableBeanFactory:综合AbstractBeanFactory并对接口AutowireCapableBeanFactory进行实现。ConfigurableListableBeanFactory:BeanFactory配置清单,指定忽略类型及接口等。DefaultListableBeanFactory:综合上面所有功能,主要是对bean注册后的处理。

XmlBeanFactory对 DefaultListableBeanFactory类进行了扩展,主要用于从XML文档中读取BeanDefinition,对于注册及获取bean都是使用从父类DefaultListableBeanFactory继承的方法去实现,而唯独与父类不同的个性化实现就是增加了XmlBeanDefinitionReader类型的reader属性。在XmlBeanFactory中主要使用reader属性对资源文件进行读取和注册。

XmlBeanDefinitionReader

实现XML配置文件读取功能

各个类的功能

ResourceLoader:定义资源加载器,主要应用于根据给定的资源文件地址返回对应的ResourceBeanDefinitionReader:主要定义资源文件读取并转换为BeanDefinition的各个功能。EnvironmentCapable:定义获取Environment方法。documentLoader:定义从资源文件加载到转换为document的功能。AbstractBeanDefinitionReader:对EnvironmentCapable,BeanDefinitionReader类定义的功能进行实现。BeanDefinitiondocumentReader:定义读取Docuemnt并注册BeanDefinition功能。BeanDefinitionParserDelegate:定义解析Element的各种方法。 处理步骤

1、通过继承自AbstractBeanDefinitionReader中的方法,来使用ResourLoader将资源文件路径转换为对应的Resource文件。
2、通过documentLoader对Resource文件进行转换,将Resource文件转换为document文件。
3、通过实现接口BeanDefinitiondocumentReader的DefaultBeanDefinitiondocumentReader类对document进行解析,并使用BeanDefinitionParserDelegate对Element进行解析

容器的基础XmlBeanFactory

分析以下功能代码实现

BeanFactory bf= new XmlBeanFactory(new ClassPathResource("be anFactoryTest.xml"));

整体分析 XmlBeanFactory初始化

用 ClassPathResource的构造函数来构造 Resource 资源文件的实例对象,这样后续的资源处理就可以用 Resource 提供的各种服务来操作

在Java中,将不同来源的资源抽象成URL,通过注册不同的handler来处理不同来源的资源的读取逻辑,但是URL没有默认定义相对Classpath或ServletContext等资源的handler,且也没有提供基本的方法,比如检查当前资源是否存在,检查当前资源是否可读的等方法。所以Spring使用Resource接口封装底层资源。

使用Resouce来进行XmlBeanFactory的初始化

利用XmlBeanDefinitionReader来获取beanDefinition

加载bean

接下来我们分析如何获取beanDefinition

处理过程:

封装资源文件。当进入XmlBeanDefinitionReader后首先对参数Resource使用EncodedResource类进行封装。获取输入流。从Resource中获取对应的InputStream并构造InputSource.通过构造的InputSource实例和Resource实例继续调用函数doLoadBeanDefinitions 源码分析 起始

BeanFactory bf= new XmlBeanFactory(new ClassPathResource("be anFactoryTest.xml"));//(1)

(1)构造 Resource 资源文件 ClassPathResource.java

public ClassPathResource(String path) {this(path, (ClassLoader) null);}public ClassPathResource(String path, @Nullable ClassLoader classLoader) {Assert.notNull(path, "Path must not be null");String pathToUse = StringUtils.cleanPath(path);if (pathToUse.startsWith("/")) {pathToUse = pathToUse.substring(1);}this.path = pathToUse;this.classLoader = (classLoader != null ? classLoader : ClassUtils.getDefaultClassLoader());}

(2)使用Resouce来进行XmlBeanFactory的初始化 XmlBeanDefinitionReader.java

public XmlBeanFactory(Resource resource) throws BeansException { this(resource, null);}//构造函数内部再次调用内部构造函数:public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {super(parentBeanFactory); //加载数据在这里完成this.reader.loadBeanDefinitions(resource);//(3)}

(3) 获取beanDefinition XmlBeanDefinitionReader.java

public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException { return loadBeanDefinitions(new EncodedResource(resource));//(2)}

EncodedResource用于对资源文件的编码进行处理,当设置了编码属性后Spring会根据相应的编码作为输入流的编码

(4) loadBeanDefinitions XmlBeanDefinitionReader.java

public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException { ... //从encodedResource中获取已经封装的Resource对象并再次从Resource中获取其中的inputstream try (InputStream inputStream = encodedResource.getResource().getInputStream()) { //通过inputStream获取inputSource InputSource inputSource = new InputSource(inputStream); //存在编码要求,进行编码设置 if (encodedResource.getEncoding() != null) { inputSource.setEncoding(encodedResource.getEncoding()); } //真正进入了逻辑核心部分 return doLoadBeanDefinitions(inputSource, encodedResource.getResource());//(3) } ...}

(5)进入逻辑核心部分 XmlBeanDefinitionReader.java

获取对XML文件的验证模式。加载XML文件,并得到对应的documento根据返回的document注册Bean信息

protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) throws BeanDefinitionStoreException {... //加载对应的document document doc = doLoaddocument(inputSource, resource);//(6) //根据doc注册Bean信息 int count = registerBeanDefinitions(doc, resource); //(8) ...}

(6)获取Xml的验证模式 XmlBeanDefinitionReader.java

protected document doLoaddocument(InputSource inputSource, Resource resource) throws Exception { //getValidationModeForResource获取Xml的验证模式 return this.documentLoader.loaddocument(inputSource, getEntityResolver(), this.errorHandler,getValidationModeForResource(resource), isNamespaceAware()); //(7)}

XML中有两种验证模式:DTD、XSD,保证XML文件的正确性

Spring 中 XmlValidationModeDetector 对验证模式的确认是循环 xml 整个文件的每一行判断是否有 DOCTYPE 字符串, 包含就是 DTD 验证模式, 不包含就是 XSD 模式

(7)获取document

Spring 中 documentLoader 有且仅有一个实现类 DefaultdocumentLoader

DefaultdocumentLoader.java

public document loaddocument(InputSource inputSource, EntityResolver entityResolver, ErrorHandler errorHandler, int validationMode, boolean namespaceAware) throws Exception { //创建documentBuilderFactory documentBuilderFactory factory = createdocumentBuilderFactory(validationMode, namespaceAware); if (logger.isTraceEnabled()) { logger.trace("Using JAXP provider [" + factory.getClass().getName() + "]"); } //创建documentBuilder documentBuilder builder = createdocumentBuilder(factory, entityResolver, errorHandler); //解析inutSource,返回document对象 return builder.parse(inputSource);}

当我们将文件转换成document后,就到接下来提取及注册bean了,我们回到(5),可以看到接下来要调用registerBeanDefinitions

protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) throws BeanDefinitionStoreException {... //加载对应的document document doc = doLoaddocument(inputSource, resource); //根据doc注册Bean信息,并获取数量 int count = registerBeanDefinitions(doc, resource); ...}

(8) 解析及注册BeanDefinitions XmlBeanDefinitionReader.java

public int registerBeanDefinitions(document doc, Resource resource) throws BeanDefinitionStoreException { // 获取 基于 document 的Bean定义读取器 BeanDefinitiondocumentReader documentReader = createBeanDefinitiondocumentReader(); // 历史已有的bean定义数量 int countBefore = getRegistry().getBeanDefinitionCount(); // 注册bean定义 documentReader.registerBeanDefinitions(doc, createReaderContext(resource)); // (9) // 注册后的数量-历史数量 return getRegistry().getBeanDefinitionCount() - countBefore;}

BeanDefinitiondocumentReader 的作用就是进行 BeanDefinition 的注册

(9) 注册bean定义 DefaultBeanDefinitiondocumentReader.java

public void registerBeanDefinitions(document doc, XmlReaderContext readerContext) { this.readerContext = readerContext; //Spring 进行 document 对象解析, 并将解析结果包装成 BeanDefinition 进行注册的核心方法 doRegisterBeanDefinitions(doc.getdocumentElement());//(10)}

前面都是XML加载解析的准备流程,接下来终于开始进行解析了

(10)doRegisterBeanDefinitions 流程 DefaultBeanDefinitiondocumentReader.java

protected void doRegisterBeanDefinitions(Element root) { // 父 BeanDefinitionParserDelegate 一开始为null BeanDefinitionParserDelegate parent = this.delegate; // 创建 BeanDefinitionParserDelegate this.delegate = createDelegate(getReaderContext(), root, parent); // 判断命名空间是否为默认的命名空间 // 默认命名空间: http://www.springframework.org/schema/beans if (this.delegate.isDefaultNamespace(root)) { // 获取 profile 属性 String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE); // 是否存在 profile if (StringUtils.hasText(profileSpec)) { // profile 切分后的数据 String[] specifiedProfiles = StringUtils.tokenizeToStringArray( profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS); if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) { return; } } }// 前置处理 preProcessXml(root); // bean definition 处理 parseBeanDefinitions(root, this.delegate); // 后置 xml 处理 postProcessXml(root); this.delegate = parent;}

流程分析:

设置父BeanDefinitionParserDelegate 对象, 值得注意的是这个设置父对象一般情况下是不存在的即 this.delegate = null创建 BeanDefinitionParserDelegate 对象 , BeanDefinitionParserDelegate 对象是作为解析的重要方法.对于 profile 属性的处理XML 解析的前置处理XML 的解析处理XML 解析的后置处理设置成员变量

补充:

这里提一个拓展点 profile 这个属性在 Spring 中一般用来做环境区分, 在 SpringBoot 中有一个类似的配置spring.profiles 、在 Spring XML 模式中 profile 是属于 的一个属性preProcessXml(root)、postProcessXml(root)这两个方法是为子类而设计的,为模版方法模式,如果需要在Bean解析前后做一些处理的话,那么只需要重写这两个类就可以了
(11)parseBeanDefinitions 分析 DefaultBeanDefinitiondocumentReader.java

protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) { // 是否是默认的命名空间 if (delegate.isDefaultNamespace(root)) { // 子节点列表 NodeList nl = root.getChildNodes(); for (int i = 0; i < nl.getLength(); i++) { Node node = nl.item(i); if (node instanceof Element) { Element ele = (Element) node; // 是否是默认的命名空间 if (delegate.isDefaultNamespace(ele)) { // 处理标签的方法 parseDefaultElement(ele, delegate); } else { // 处理自定义标签 delegate.parseCustomElement(ele); } } } } else { // 处理自定义标签 delegate.parseCustomElement(root); }}

parseBeanDefinitions 方法主要是对一个 Element 的每个节点进行处理, 节点本身又存在多样性,

节点的多样性

Spring 提供的标签: 即 DTD 或者 XSD 中定义的标签
如:自定义标签
如:

根据节点多样性 Spring 提供了两个方法进行处理 parseDefaultElement 和 delegate.parseCustomElement(ele)

参考链接:

第三章 IoC 资源读取及注册

python语言属于什么语言?Python特点主要有哪些?
目前,Python已经成为求职中最吃香的三大编程语言之一,而且相较于C、C++、Java等语言而言,Python拥有非常独特的优势,通过极少的代码量就可以实现相同的功能,可以有效提升开发者的
python虚拟环境打包exe
一、安装虚拟环境 进入cmd环境, C:Userscjy>pip install virtualenv -i https://pypi.tuna.tsinghua.edu.cn/simple some-package 二、创建虚拟环境 virtualenv
[Spring源码]——容器基本实现
笔记整理自《Spring源码深度解析》(第2版),同时也参考了一些网上资源,具体参考链接在文末 文章目录 核心类介绍 DefaultlistableBeanFactoryXmlBeanDefinitionRead
搭建harbor仓库
1.安装docker  yum remove docker                   docker-client                   docker-client-latest                  
369问题网
免责声明:问题365网所有文字、图片、视频、音频等资料均来自互联网,不代表本站赞同其观点,内容仅代表作者本人意见,若因此产生任何纠纷作者本人负责,本站亦不为其版权负责! 如有问题,请联系我们
CopyRight©1999-2022 www.wt369.net/ All Right Reserved